﻿using ArtMath.Attribute;
using ArtMath.Component.Point;
using ArtMath.Core.Data;
using ArtMath.Core.Helper;
using ArtMath.Data;
using ArtMath.PointCollection;
using ArtMath.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ArtMath.Component.Vector
{
    /// <summary>
    /// 约束向量
    /// </summary>
    [Serializable]
    public class ConstrainedVector : VectorBase
    {
        PadStringX len;
        AngleStringX angle;
        // 在修改长度和方向角时，由于需要移动其中一个点，所以需要此变量来指示本元件对该点的无条件允许移动
        bool permit;
        public ConstrainedVector(SketchPad pad, string name, string dirAngle, string len, bool addToList = true) : base(pad, name, ToolTypeName.ysxl, addToList)
        {
            StrLength = new PadStringX(Pad, len ?? "1");
            StrDirectedAngle = new AngleStringX(Pad, dirAngle ?? "0");
        }
        void Length_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            if (StrLength.Value < 0)
            {
                Pad.ShowMessage(Resources.JoinLocalizedString("LSLength", "MustNonNegative"), MessageType.Error);
                StrLength.SetExpression(e.OldExpression, false);
            }
            else
            {
                Length = StrLength.Value;
                if (tryRelocateAnotherPoint(DirectedAngle, Length))
                {

                }
                else
                {
                    Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("LSLength")), MessageType.Error);
                    StrLength.SetExpression(e.OldExpression, false);
                    Length = e.OldValue;
                }
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }
        void DirectedAngle_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            DirectedAngle = StrDirectedAngle.RadValue;
            if (tryRelocateAnotherPoint(DirectedAngle, Length))
            {

            }
            else
            {
                Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("DirectedAngle")), MessageType.Error);
                StrDirectedAngle.SetExpression(e.OldExpression, false);
                DirectedAngle = Pad.GetRad(e.OldValue);
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("VecLength")]
        public PadStringX StrLength
        {
            get { return len; }
            set
            {
                if (len is object)
                {
                    len.ParametersValueChanging -= Length_ParametersValueChanging;
                    len.ParametersValueChanged -= Length_ParametersValueChanged;
                    len.ExpressionSetting -= Length_ExpressionSetting;
                    len.ExpressionSet -= Length_ExpressionSet;
                }
                len = value;
                if (len is object)
                {
                    len.ParametersValueChanging += Length_ParametersValueChanging;
                    len.ParametersValueChanged += Length_ParametersValueChanged;
                    len.ExpressionSetting += Length_ExpressionSetting;
                    len.ExpressionSet += Length_ExpressionSet;
                }
            }
        }

        void Length_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            if (StrLength.Value >= 0)
            {
                Length = StrLength.Value;
                if (tryRelocateAnotherPoint(DirectedAngle, Length))
                {

                }
                else
                {
                    Length = e.OldValue;
                }
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }

        void Length_ParametersValueChanging(DocStringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        void Length_ExpressionSetting(StringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("DirectedAngle")]
        public AngleStringX StrDirectedAngle
        {
            get { return angle; }
            set
            {
                if (angle is object)
                {
                    angle.ParametersValueChanging -= DirectedAngle_ParametersValueChanging;
                    angle.ParametersValueChanged -= DirectedAngle_ParametersValueChanged;
                    angle.ExpressionSetting -= DirectedAngle_ExpressionSetting;
                    angle.ExpressionSet -= DirectedAngle_ExpressionSet;
                }
                angle = value;
                if (angle is object)
                {
                    angle.ParametersValueChanging += DirectedAngle_ParametersValueChanging;
                    angle.ParametersValueChanged += DirectedAngle_ParametersValueChanged;
                    angle.ExpressionSetting += DirectedAngle_ExpressionSetting;
                    angle.ExpressionSet += DirectedAngle_ExpressionSet;
                }
            }
        }

        void DirectedAngle_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            DirectedAngle = StrDirectedAngle.RadValue;
            if (tryRelocateAnotherPoint(DirectedAngle, Length))
            {

            }
            else
            {
                DirectedAngle = Pad.GetRad(e.OldValue);
            }
            Pad.MoveObject_DoMouseUp(Point2.Movable ? Point2 : Point1);
        }

        void DirectedAngle_ParametersValueChanging(DocStringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }

        void DirectedAngle_ExpressionSetting(StringX sender)
        {
            Pad.MoveObject_DoMouseDown(Point2.Movable ? Point2 : Point1);
        }
        public override int ClickTimes
        {
            get
            {
                return 1;
            }
        }
        public override double NewSlope
        {
            get
            {
                return Math.Tan(StrDirectedAngle.RadValue);
            }
        }
        public override double NewDirectedAngle
        {
            get
            {
                return StrDirectedAngle.RadValue;
            }
        }
        public override double NewLength
        {
            get
            {
                return StrLength.Value; ;
            }
        }
        public override bool PointsHaveConstraint
        {
            get { return true; }
        }
        bool tryRelocateAnotherPoint(double angle, double len)
        {
            permit = true;
            bool res = false;
            if (Point2.Movable)
            {
                Point1.TempMoveLock = true;
                res = Pad.MoveObject_DoMouseMove(Point2, Pad.XYToPxy(Point1.ToPointD().Offset(angle, len)));
                Point1.TempMoveLock = false;
            }
            else if (Point1.Movable)
            {
                Point2.TempMoveLock = true;
                res = Pad.MoveObject_DoMouseMove(Point1, Pad.XYToPxy(Point2.ToPointD().BeforeOffset(angle, len)));
                Point2.TempMoveLock = false;
            }
            permit = false;
            return res;
        }
        public override PointCollectionBase[] GetInPointMovingRange(PointBase movePoint, Base sender = null)
        {
            if (!permit)
            {
                switch (CountMovablePoints())
                {
                    case 1:
                        return new PointCollectionBase[] { new DiscontinuityPoint(movePoint == Point1 ? Point2.NewXY.BeforeOffset(NewDirectedAngle, NewLength) : Point1.NewXY.Offset(NewDirectedAngle, NewLength)) };
                }
            }
            return base.GetInPointMovingRange(movePoint, sender);
        }
        public override void CreateComponents()
        {
            if (Point1 == null)
            {
                CreateComponents(Resources.Translate("StartPoint"));
                CreateComponents(Pad.CreatePoint(Resources.Translate("TerminalPoint"), Pad.XYToPxy(Point1.ToPointD().Offset(StrDirectedAngle.RadValue, StrLength.Value)), false));//此处已调用base.CreateComponents();
            }
        }
        public override object CopyAndPaste(SketchPad pad)
        {
            ConstrainedVector cv = new ConstrainedVector(pad, Utility.GetNextAvailableName(Name, pad.CheckObjectNameExist), StrDirectedAngle.DirectExpression, StrLength.DirectExpression, LayerItem != null);
            afterNewCopy(cv);
            return cv;
        }
    }
}
